/** 
 * @file Atributo.java
 * 
 * @author Luis Ernesto Carrera
 *
 * @date 2009-08-20
 */

package estadistec.logica.tabla;

import estadistec.logica.tipos.*;

import estadistec.logica.dependencias.Nodo;
import estadistec.logica.parser.EstructuraEvaluable;
import estadistec.logica.parser.IEvaluable;

import estadistec.logica.gestores.Gestor;

import estadistec.excepciones.ExcepcionFormatoNumeros;
import estadistec.excepciones.ExcepcionCapacidadIndiceFueraRango;

import java.util.Hashtable;

/**
 * Implementación del atributo. Contiene una instancia de 
 * {@link estadistec.logica.tipos.Valores}, que
 * es donde se guardan los valores y el tipo del atributo.
 *
 * Esta clase maneja además las dependencias entre atributos.
 */
public class Atributo extends Nodo implements IEvaluable
{

	/**
	 * Arreglo de los valores.
	 */
	private Valores _valores;

	/**
	 * Tabla lógica en la cual se encuentra insertado este atributo.
	 */
	private LTabla _lTabla;

	/**
	 * Estructura que permite asignar y recalcular los valores.
	 */
	private EstructuraEvaluable _formula;

	/**
	 * Texto de la fórmula.
	 */
	private String _textoFormula;

	/** 
	 * Devuelve el número de casos. Requerido por IEvaluable.
	 */
	public int getNoCasos() {
		return this._lTabla.getNoCasos();
	}

	/** 
	 * Devuelve el valor requerido. Requerido por IEvaluable.
	 */
	public Object getValor(int indice) {
		return this._valores.getValor(indice);
	}

	/** 
	 * Constructor. Construye un atributo con el número de casos dados.
	 * 
	 * @param tipoValor Tipo del atributo.
	 * @param noCasos Número de casos de la tabla.
	 * @param lTabla  Tabla a la que pertence este atributo
	 * @param llave identificador único para uso interno
	 */
	public Atributo (
			TipoValor tipoValor,
			String nombre,
			int noCasos,
			LTabla lTabla,
			Long llave)
	{
		switch (tipoValor) {
			case BOOLEANO:
				_valores = new Booleanos(noCasos);
				break;
			case ENTERO:
				_valores = new Enteros(noCasos);
				break;
			case FLOTANTE:
				_valores = new Flotantes(noCasos);
				break;
			case INTERVALO:
				_valores = new Intervalos(noCasos);
				break;
			case CUALITATIVO:
				_valores = new Cualitativos(noCasos);
				break;
			default:
				assert (false);
		}
		this.setNombre(nombre);
		this._lTabla = lTabla;
		this._dependientes = new Hashtable<Long, Nodo>();
		this._superiores = new Hashtable<Long, Nodo>();
		this._textoFormula = null;
		this._llave = llave;
	}


	/**
	 * Actualiza sus valores. Envia a actualizar sus valores, puesto que el
	 * gestionable del que depende ha cambiado. Es utilizado por actualizar(),
	 * quien se encarga también de actualizar a los dependientes.
	 */
	protected void actualizarValor()
	{
		this._formula.evaluar(this);
		this._lTabla.actualizarValores(this.getLlave(), this.getTextoValores());
	}


	/*
	 * ///////////// Métodos del Atributo \\\\\\\\\\\\\
	 */
	/**
	 * Devuelve el tipo del atributo. 
	 *
	 * @see estadistec.logica.tipos.TipoValor#TipoValor
	 */
	public TipoValor getTipo()
	{
		return this._valores.getTipo();
	}


	/***
	 * Cantidad de decimales que usa para los tipos con punto
	 * flotante: FLOTANTE e INTERVALO.
	 * 
	 * @return Número de decimales
	 * @throws NoSuchMethodException En caso de que el tipo del atributo
	 * no sea FLOTANTE o INTERVALO.
	 */
	public int getNoDecimales() throws NoSuchMethodException
	{
		return this._valores.getNoDecimales();
	}

	/**
	 * Cambia el número de decimales que se muestran. Se utiliza para aquellos
	 * tipos que utilizan punto flotante ({@link estadistec.logica.tipos.Flotantes} 
	 * e {@link estadistec.logica.tipos.Intervalos}). 
	 * 
	 * @param noDecimales Cantidad de decimales después del punto.
	 *
	 * @return Un arreglo con el texto de cada uno de los valores en caso de
	 * que se haya cambiado el número de decimales, y si el número es el mismo
	 * al que había, se devuelve <em>null</em>.
	 *
	 * @see estadistec.logica.tipos.Valores#setNoDecimales para excepciones.
	 */
	public void setNoDecimales(int noDecimales)
	{
		this._valores.setNoDecimales(noDecimales);
	}


	/**
	 * Devuelve el texto de un valor específico. El índice del valor
	 * debe ser 0-indexado.
	 */
	public String getTextoValor(int indice) {
		return this._valores.getTexto(indice);
	}

	/**
	 * Devuelve en un arreglo el texto de todos los valores del atributo.
	 */
	public String[] getTextoValores()
	{
		return this._valores.getTextoValores();
	}

	/**
	 * Asigna una nueva fórmula. Ya se debe de haber inicializado el atributo,
	 * es decir, Java debe encargarse de determinar el tipo del atributo. Se
	 * utiliza el método prefijo, en el cual se colocan las funciones, y para
	 * cada función los argumentos correspondientes.
	 * 
	 * @param formula Estructura evaluable que permite generar los valores
	 * y actualizarlos.
	 * 
	 * @return El vector del texto de los valores.
	 */
	public String[] setFormula(EstructuraEvaluable formula)
	{
		this._formula = formula;
		this._formula.evaluar(this);
		return (this.getTextoValores());
	}

	/**
	 * Asigna un valor a una celda en particular a partir de un texto.
	 * 
	 * @param textoValor
	 *            Valor a establecer.
	 * @param indice
	 *            Número de fila 0-indexada.
	 *
	 * @return El texto del valor asignado.
	 *
	 * Para excepciones: @see Valores.set.
	 */
	public String set(int indice, String textoValor) 
	{
		// Si se envia un string vacío, entonces lo que se quiere es eliminar
		// el valor.
		if (textoValor.trim().equals("")) {
			this.borrar(indice, 1);
			return (new String());
		}
		// El valor enviado no está vacío.
		return this._valores.set(indice, textoValor);
	}

	/**
	 * Borra el valor de un rango de casos.
	 * 
	 * @param indice
	 *            Inicio del borrado, 0-indexado.
	 *
	 * @param noCasos
	 *            Cantidad de valores a borrar a partir del índice.
	 * 
	 * Excepciones @see Valores.borrar
	 */
	public void borrar(int indice, int noCasos)
	{
		this._valores.borrar(indice, noCasos);
	}


	/**
	 * Insertar casos en el atributo. Observe que no se actualiza acá.
	 * Ello porque insertar casos lo realiza LTabla. Mejor que ella
	 * se encarge de asignar quien se actualiza.
	 * 
	 * @param indice
	 *            Índice inicial, 0-indexada.
	 * @param noCasos
	 *            Número de casos a insertar.
	 */
	public void insertarCasos(int indice, int noCasos) 
	{
		this._valores.insertarCasos(indice, noCasos);
	}

	/**
	 * Elimina un rango de casos.
	 * 
	 * @param indice
	 *            Índice inicial, 0-indexado.
	 * @param noCasos
	 *            Cantidad de casos a eliminar.
	 * 
	 * @see Valores @p eliminarCasos para las excepciones.
	 *
	 * @todo Falta actualizar a los dependientes de manera más eficiente. En
	 * caso de que dos atributos modificados tengan el mismo dependiente,
	 * dicho dependiente se actualiza dos veces. Tratar de resolver el
	 * problema de manera general, guardando un conjunto de atributos que
	 * hay que actualizar, y actualizándolos al final, haciendo un orden
	 * para que todo el mundo se actualice bien.
	 */
	public void eliminarCasos(int indice, int noCasos) 
	{
		this._valores.eliminarCasos(indice, noCasos);
		/* Actualiza su valor en caso de que se defina de manera iterativa
		 * y actualiza además el valor de los depenientes. */
		if (!this.esIndependiente())
			this.actualizar();
	}

	/**
	 * Define el valor por defecto del atributo.
	 * 
	 * @param textoValor
	 *            Valor por defecto.
	 * @throws ExcepcionFormatoNumeros
	 *             El texto no puede leerse como tipo del atributo definido.
	 *
	 * Para excepciones @see Valores.setValorXDefecto.
	 */
	public void setValorXDefecto(String textoValor)
		throws ExcepcionFormatoNumeros
	{
		this._valores.setValorXDefecto(textoValor);
	}

	public void setValor (int indice, Object valor) {
		this._valores.setValor(indice, valor);
	}

	/**
	 * Define a blanco como valor por defecto.
	 */
	public void quitarValorXDefecto()
	{
		this._valores.quitarValorXDefecto();
	}

	/**
	 * Método requerido por IManejable.
	 */
	public Gestor getGestor() {
		return this._lTabla;
	}

	/** 
	 * Devuelve los valores.
	 */
	public Valores getValores() {
		return (this._valores);
	}

	/**
	 * Devuelve el texto de la fórmula de la cual depende este atributo.
	 *
	 * @todo Debe modificarse la referencia a cada uno de los atributos por
	 * una su nombre.
	 */
	public String getTextoFormula()
	{
		return this._textoFormula;
	}

	/**
	 * Guarda la fórmula digitada por el usuario.
	 *
	 * @todo Debe modificarse cada nombre por la referencia respectiva.
	 */
	public void setTextoFormula(String formulaDigitada)
	{
		this._textoFormula = formulaDigitada;
	}

	void deleteFormula() {
		this._textoFormula = null;
		this._formula = null;
	}
}
